package sh.orm;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import sh.base.aop.ProxyManager;
import sh.base.aop.TargetSource;
import sh.base.aop.util.ReflectUtil;
import sh.orm.annotation.NativeSql;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;

/**
 * orm中生成代理的工厂
 * 获取对应的dao，已经是经过代理的
 * @author lqq
 * @date 2019/2/24
 */
@Slf4j
public class ProxyFactory {

    /**
     * 获取关联关系中对象的代理
     * 生成代理对象，关联查询的对象进行代理，才能二次调用数据库。
     * @param entity
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getRelationProxy(Object entity, Class<T> clazz){

        //  使用aop联盟获取代理对象

        ProxyManager proxyManager = new ProxyManager();
        proxyManager.setTargetSource(new TargetSource(entity,clazz,null));
        RelationInterceptor relationInterceptor = new RelationInterceptor();
        proxyManager.setMethodInterceptor(relationInterceptor);

        Object proxy = proxyManager.getProxy();
        //需要把代理设置进去
        relationInterceptor.setProxy(proxy);
        return (T)proxy;
    }




    /**
     * 获取dao的代理
     * @param daoInterface
     * @param <T>
     * @return
     */
    public static <T> T getDao(Class<T> daoInterface){

        //  使用aop联盟获取代理对象

        Class<?>[] interfaces = new Class<?>[1];
        interfaces[0] = daoInterface;
        ProxyManager proxyManager = new ProxyManager();
        proxyManager.setTargetSource(new TargetSource(null,null,interfaces));
        proxyManager.setMethodInterceptor(new DaoInterceptor());

        return (T)proxyManager.getProxy();
    }


    /**
     * 拦截dao的操作
     */
    static class DaoInterceptor implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            String sql = null;
            for(Annotation annotation : invocation.getMethod().getAnnotations()){
                if(annotation instanceof NativeSql){
                    NativeSql nativeSql = (NativeSql)annotation;
                    sql = nativeSql.value();
                    break;
                }
            }
            if(sql == null){
                throw  new OrmException("没有解析到sql" + invocation.getMethod().getDeclaringClass().getSimpleName()
                        +  invocation.getMethod().getName());
            }

            sql = String.format(sql, invocation.getArguments());
            log.info("代理的sql ======== 》" + sql);

            Class<?> returnType = invocation.getMethod().getReturnType();
            if(returnType.equals(List.class)){
                // 获取List<T> 中泛型的类型
                Type genericReturnType = invocation.getMethod().getGenericReturnType();
                Class<?> genericType = ReflectUtil.getListGenericClass(genericReturnType);
                Session session = SessionFactory.getInstance().getSession();
                return session.query(sql,genericType);
            }

            throw new OrmException("暂不支持的类型" + returnType.getSimpleName()) ;
        }
    }


    /**
     * 关联关系中的拦截操作
     */
    static class RelationInterceptor implements MethodInterceptor{

        /**
         * 生成代理对象之后，反过来把代理对象赋值到这里，
         * 这样才能在业务调用中使用代理对象
         */
        @Setter
        private Object proxy;

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            //如果已经执行过查询，直接返回属性里面的值 TODO 如果查询的结果为空，如何标记已经执行过查询了
            //如果没有执行查询，从数据库里进行查询
            //拿到当前的连接继续执行查询操作

            Session session = SessionFactory.getInstance().getSession();

            //执行查询：注解，关联字段，当前对象的id
            //处理结果集，返回值类型，

            return session.query(invocation.getMethod(),proxy);
        }
    }

}
